package com.swensen.game.data;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import com.swensen.game.data.Tile.TileId;
import com.swensen.game.data.Tile.TileType;

/**
 * The Class TileLoader.
 */
class TileLoader {
	
	/**
	 * The Class TileLoadingData.
	 */
	public static class TileLoadingData {
		
		/** The file. */
		private final File file;
		
		/** The type. */
		private final TileType type;
		
		/**
		 * Instantiates a new tile loading data.
		 *
		 * @param file the file
		 * @param type the type
		 */
		public TileLoadingData(File file, TileType type) {
			this.file = file;
			this.type = type;
		}
		
		/**
		 * Gets the file.
		 *
		 * @return the file
		 */
		public File getFile() {
			return file;
		}
		
		/**
		 * Gets the type.
		 *
		 * @return the type
		 */
		public TileType getType() {
			return type;
		}
	}
	
	/**
	 * Load basic tiles.
	 *
	 * @param singleData the single data
	 * @param tileData 
	 * @return the collection<? extends tile>
	 */
	private static List<BasicTile> loadBasicTiles(
			TileLoadingData singleData, Map<String, List<BufferedImage>> tileData) {
		File file = singleData.getFile();
		FileInputStream stream = null;
		try {
			stream = new FileInputStream(file);
			BufferedImage image = ImageIO.read(stream);
			
			int height = image.getHeight();
			int width = image.getWidth();
			
			if (width % Tile.TILE_SIZE != 0 || width == 0 ||
				height % Tile.TILE_SIZE != 0 || height == 0) {
				throw new InvalidParameterException("Basic tile image width and/or " +
						"height is invalid.");
			}
			
			List<BasicTile> tiles = new ArrayList<BasicTile>();
			
			int pos = 0;
			int rows = height / Tile.TILE_SIZE;
			int cols = width / Tile.TILE_SIZE;
			
			BufferedImage[] images = new BufferedImage[rows * cols];
			
			for (int i = 0; i < rows; ++i) {
				for (int j = 0; j < cols; ++j) {
					images[pos] = parseImage(image, Tile.TILE_SIZE, Tile.TILE_SIZE, i, j);
					tiles.add(new BasicTile(new TileId(singleData.file.getName(), pos++)));
				}
			}
			
			tileData.put(singleData.getFile().getName(), Arrays.asList(images));
			return tiles;
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			if (stream != null) {
				try {
					stream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		return new ArrayList<BasicTile>();
	}

	/**
	 * Load tiles.
	 *
	 * @param data the data
	 * @param tileData 
	 * @return the list
	 */
	public static List<Tile> loadTiles(List<TileLoadingData> data, Map<String, List<BufferedImage>> tileData) {
		List<Tile> tiles = new ArrayList<Tile>();
		for(TileLoadingData singleData : data) {
			tiles.addAll(loadTiles(singleData, tileData));
		}
		Collections.sort(tiles);
		return tiles;
	}

	/**
	 * Load tiles.
	 *
	 * @param singleData the single data
	 * @return the collection<? extends tile>
	 */
	private static Collection<? extends Tile> loadTiles(
			TileLoadingData singleData, Map<String, List<BufferedImage>> tileData) {
		switch(singleData.getType()) {
		case ANIMATED:
			List<AnimatedTile> tiles = new ArrayList<AnimatedTile>();
			tiles.add(new AnimatedTile(new TileId(singleData.file.getName(), -1), loadBasicTiles(singleData, tileData)));
			return tiles;
		case BASIC:
			return loadBasicTiles(singleData, tileData);
		case FILL:
			break;
		default:
			break;
		}
		return new ArrayList<Tile>();
	}

	/**
	 * Parses the image.
	 *
	 * @param image the image
	 * @param width the width
	 * @param height the height
	 * @param y the y
	 * @param x the x
	 * @return the buffered image
	 */
	private static BufferedImage parseImage(BufferedImage image, int width,
			int height, int y, int x) {
		
		BufferedImage partialImage = new BufferedImage(width, height, image.getType());
		Graphics2D graphics = partialImage.createGraphics();
		graphics.drawImage(image, 0, 0, width, height, width * x, height * y,
				width * x + width, height * y + height, null);
		graphics.dispose();
		
		return partialImage;
	}
}
